/*
 * Copyright 2011-17 Fraunhofer ISE, energy & meteo Systems GmbH and other contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.openmuc.openiec61850;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.sun.org.apache.xpath.internal.operations.Mod;
import org.openmuc.openiec61850.internal.mms.asn1.Data;
import org.openmuc.openiec61850.internal.mms.asn1.Identifier;
import org.openmuc.openiec61850.internal.mms.asn1.TypeDescription;
import org.openmuc.openiec61850.internal.mms.asn1.TypeDescription.Structure;
import org.openmuc.openiec61850.internal.mms.asn1.TypeDescription.Structure.Components;
import org.openmuc.openiec61850.internal.mms.asn1.TypeSpecification;

public abstract class ModelNode implements Iterable<ModelNode> {

    protected ObjectReference objectReference;
    protected Map<String, ModelNode> children;
    ModelNode parent;

    /**
     * Returns a copy of model node with all of its children. Creates new BasicDataAttribute values but reuses
     * ObjectReferences, FunctionalConstraints.
     *
     * @return a copy of model node with all of its children.
     */
    public abstract ModelNode copy();

    /**
     * Returns the child node with the given name. Will always return null if called on a logical node because a logical
     * node need the functional constraint to uniquely identify a child. For logical nodes use
     * <code>getChild(String name, Fc fc)</code> instead.
     *
     * @param name
     *            the name of the requested child node
     * @return the child node with the given name.
     */
    public ModelNode getChild(String name) {
        return getChild(name, null);
    }

    /**
     * Returns the child node with the given name and functional constraint. The fc is ignored if this function is
     * called on any model node other than logical node.
     *
     * @param name
     *            the name of the requested child node
     * @param fc
     *            the functional constraint of the requested child node
     * @return the child node with the given name and functional constrain
     */
    public ModelNode getChild(String name, Fc fc) {
        return children.get(name);
    }

    @SuppressWarnings("unchecked")
    public Collection<ModelNode> getChildren() {
        if (children == null) {
            return null;
        }
        return (Collection<ModelNode>) ((Collection<?>) children.values());
    }

    public List<ModelNode> getChildList() {
        List<ModelNode> childList = new LinkedList<ModelNode> ();
        List<String> keyList = new ArrayList<String>(children.keySet());
        for(String key : keyList){
            childList.add(this.getChild(key));
        }

        return childList;
    }

    protected Iterator<Iterator<? extends ModelNode>> getIterators() {
        List<Iterator<? extends ModelNode>> iterators = new ArrayList<Iterator<? extends ModelNode>>();
        if (children != null) {
            iterators.add(children.values().iterator());
        }
        return iterators.iterator();
    }

    /* 找到一颗树中某个节点 */
    public ModelNode findModeNodeByObjectReference(ObjectReference objectReference) {
        //顺序遍历，第一步this.objectReference肯定为空，objectReference不一定为空
        if(this.objectReference == null) {
             if (objectReference==null){
                 return this;
             }
        }else if (this.objectReference.toString().replaceAll("\\W","").equals(objectReference.toString())) {
                return this;
        }

        if (children == null || children.isEmpty()) {
            return null;
        } else {
            for(String key : children.keySet()){
                ModelNode child = children.get(key);
                ModelNode resultNode = child.findModeNodeByObjectReference(objectReference);
                if (resultNode != null) {
                    return resultNode;
                }
            }
            return null;
        }
    }

    /* 返回当前节点的父辈节点集合 */
    public List<ModelNode> getElders() {
        List<ModelNode> elderList = new ArrayList<ModelNode>();
        ModelNode parentNode = this.getParent();
        if (parentNode == null) {
            return elderList;
        } else {
            elderList.add(parentNode);
            elderList.addAll(parentNode.getElders());
            return elderList;
        }
    }

    /* 返回当前节点的晚辈集合 */
    public List<ModelNode> getJuniors() {
        List<ModelNode> juniorList = new ArrayList<ModelNode>();
        List<ModelNode> childList = this.getChildList();
        if (childList == null) {
            return juniorList;
        } else {
            int childNumber = childList.size();
            for (int i = 0; i < childNumber; i++) {
                ModelNode junior = childList.get(i);
                juniorList.add(junior);
                juniorList.addAll(junior.getJuniors());
            }
            return juniorList;
        }
    }

    /**
     * Returns the reference of the model node.
     *
     * @return the reference of the model node.
     */
    public ObjectReference getReference() {
        return objectReference;
    }

    /**
     * Returns the name of the model node.
     *
     * @return the name of the model node.
     */
    public String getName() {
        return objectReference.getName();
    }

    @Override
    public Iterator<ModelNode> iterator() {
        return children.values().iterator();
    }

    /**
     * 返回所有叶子节点数据属性和值的列表
     * Returns a list of all leaf nodes (basic data attributes) contained in the subtree of this model node.
     *
     * @return a list of all leaf nodes (basic data attributes) contained in the subtree of this model node.
     */
    public List<BasicDataAttribute> getBasicDataAttributes() {
        List<BasicDataAttribute> subBasicDataAttributes = new LinkedList<BasicDataAttribute>();
        for (ModelNode child : children.values()) {
            subBasicDataAttributes.addAll(child.getBasicDataAttributes());
        }
        return subBasicDataAttributes;
    }

    @Override
    public String toString() {
        return getReference().toString();
    }

    void setParent(ModelNode parent) {
        this.parent = parent;
    }

    /**
     * Returns the parent node of this node.
     *
     * @return the parent node of this node.
     */
    public ModelNode getParent() {
        return parent;
    }

    Data getMmsDataObj() {
        return null;
    }

    void setValueFromMmsDataObj(Data data) throws ServiceError {
    }

    TypeDescription getMmsTypeSpec() {

        Components componentsSequenceType = new Components();

        List<TypeDescription.Structure.Components.SEQUENCE> structComponents = componentsSequenceType.getSEQUENCE();
        for (ModelNode child : children.values()) {
            TypeSpecification typeSpecification = new TypeSpecification();
            typeSpecification.setTypeDescription(child.getMmsTypeSpec());

            TypeDescription.Structure.Components.SEQUENCE component = new TypeDescription.Structure.Components.SEQUENCE();
            component.setComponentName(new Identifier(child.getName().getBytes()));
            component.setComponentType(typeSpecification);

            structComponents.add(component);
        }

        Structure structure = new Structure();
        structure.setComponents(componentsSequenceType);

        TypeDescription typeDescription = new TypeDescription();
        typeDescription.setStructure(structure);

        return typeDescription;
    }

}
